package com.yejq.web.servlet;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yejq.web.model.Attribute;
import com.yejq.web.model.Request;
import com.yejq.web.model.RequestHolder;
import com.yejq.web.model.RequestMapping;
import com.yejq.web.model.RequestMethod;
import com.yejq.web.model.ReturnModel;
import com.yejq.web.model.SessionAttribute;
import com.yejq.web.util.PropertiesUtil;

@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {

	private Request request = RequestHolder.getRequest();
	private RequestMethod requestMethod = RequestMethod.GET;

	private Logger logger = LoggerFactory.getLogger(getClass());

	private SimpleDateFormat dateFormat = new SimpleDateFormat(PropertiesUtil.getProperty("dateTimeFormat"));

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String requestURI = req.getRequestURI();
		String contextPath = req.getContextPath();
		requestURI = requestURI.substring(requestURI.indexOf(contextPath) + contextPath.length());

		RequestMapping mapping = request.get(requestURI + "#" + requestMethod.toString());
		if (mapping == null) {
			mapping = request.get(requestURI + "#ALL");
		}
		if (mapping != null) {
			try {
				Class<?>[] parameterTypes = mapping.getParameterTypes();
				String[] paramNames = mapping.getParamNames();
				Object[] args = new Object[parameterTypes.length];
				for (int i = 0; i < parameterTypes.length; i++) {
					Class<?> parameter = parameterTypes[i];
					String simpleName = parameter.getSimpleName();
					switch (simpleName) {
					case "String":
						args[i] = req.getParameter(paramNames[i]);
						break;
					case "int":
						args[i] = Integer.parseInt(req.getParameter(paramNames[i]));
						break;
					case "Integer":
						args[i] = Integer.parseInt(req.getParameter(paramNames[i]));
						break;
					case "Date":
						String date = req.getParameter(paramNames[i]);
						if (date.length() == 10) {
							dateFormat = new SimpleDateFormat(PropertiesUtil.getProperty("dateFormat"));
						}
						args[i] = dateFormat.parseObject(date);
						break;
					case "Double":
						args[i] = Double.parseDouble(req.getParameter(paramNames[i]));
						break;
					case "double":
						args[i] = Double.parseDouble(req.getParameter(paramNames[i]));
						break;
					default:
						Object paraObject = parameter.newInstance();
						Field[] declaredFields = parameter.getDeclaredFields();
						for (Field field : declaredFields) {
							field.setAccessible(true);
							Class<?> type = field.getType();
							switch (type.getSimpleName()) {
							case "int":
								field.set(paraObject, Integer.parseInt(req.getParameter(field.getName())));
								break;
							case "Integer":
								field.set(paraObject, Integer.parseInt(req.getParameter(field.getName())));
								break;
							case "Double":
								field.set(paraObject, Double.parseDouble(req.getParameter(field.getName())));
								break;
							case "double":
								field.set(paraObject, Double.parseDouble(req.getParameter(field.getName())));
								break;
							case "Date":
								String dateInner = req.getParameter(field.getName());
								if (dateInner.length() == 10) {
									dateFormat = new SimpleDateFormat(PropertiesUtil.getProperty("dateFormat"));
								}
								field.set(paraObject, dateFormat.parseObject(dateInner));
								break;	
								
							default:
								field.set(paraObject, req.getParameter(field.getName()));
								break;
							}
							field.setAccessible(false);
						}
						args[i] = paraObject;
						break;
					}
				}

				Class<?> clazz = Class.forName(mapping.getClassName());
				Method method = clazz.getDeclaredMethod(mapping.getMethodName(), parameterTypes);
				Object object = clazz.newInstance();
				ReturnModel returnModel = (ReturnModel) method.invoke(object, args);
				List<Attribute> attributes = returnModel.getAttributes();
				if (attributes != null && attributes.size() > 0) {
					for (Attribute attribute : attributes) {
						req.setAttribute(attribute.getKey(), attribute.getValue());
					}
				}
				Attribute attribute = returnModel.getAttribute();
				if (attribute != null) {
					req.setAttribute(attribute.getKey(), attribute.getValue());
				}
				SessionAttribute sessionAttribute = returnModel.getSessionAttribute();
				if (sessionAttribute != null) {
					HttpSession session = req.getSession();
					session.setAttribute(sessionAttribute.getKey(), sessionAttribute.getValue());
				}
				
				String url = returnModel.getUrl();
				if (url != null && url.length() > 0) {
					if (url.startsWith("redirect:")) {
						url = url.substring("redirect:".length());
						resp.sendRedirect(url);
					}else {
						RequestDispatcher rd =  req.getRequestDispatcher(url);
						rd.forward(req, resp);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			logger.error("no mapping for url:{}", requestURI);
		}
	}

	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.DELETE;
		this.doGet(req, resp);
	}

	@Override
	protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.HEAD;
		this.doGet(req, resp);
	}

	@Override
	protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.OPTIONS;
		this.doGet(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.POST;
		this.doGet(req, resp);
	}

	@Override
	protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.PUT;
		this.doGet(req, resp);
	}

	@Override
	protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		requestMethod = RequestMethod.TRACE;
		this.doGet(req, resp);
	}

	@Override
	protected long getLastModified(HttpServletRequest req) {
		return super.getLastModified(req);
	}

	@Override
	protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
		super.service(arg0, arg1);
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		super.service(req, res);
	}

}
